// ****************************************************************************
// @File: WrapperManager.h
//
// PURPOSE: A wrapper manager to store CObjectWrapper objects
// AUTHOR:  Zj Ai
//
// NOTES: 
//     
// HISTORY:
//
//   ------ Zj Ai  05/07/2012 creating the file
//
// @EndHeader@
// ****************************************************************************
#pragma once
#include "stdafx.h"
#include <list>
#include <map>
#include "ObjectWrapper.h"
#include "SingleObjWrapper.h"
#include "Lock.h"

// if we are not using the pool, de-comment the following line
#define USING_POOL

#ifdef 	USING_POOL
#include "Pool.h"
#endif

class  CWrapperManager 
{
private:
	typedef std::map<long, CObjectWrapper*> WrapperMap;
	WrapperMap m_wrappers;

	// this is the shortcut for using the NULL pointer
	CObjectWrapper* m_pNullWrapper;

	// size of extra bytes more than requested allocated by new.
	static int extraSize;

	// store the instance
	static CWrapperManager *m_sWrapperManager;
public:
	static CWrapperManager* GetInstance();
	static void Destroy();

	void Insert(void* pRawPointer, CObjectWrapper* pWrapper);
	void Remove(CObjectWrapper* pWrapper);
	
	// return the ref count of the pWrapper
	int GetRefCount(void* p);
	// return the objects that this manager in charge with
	int GetObjectCount();
	
	/**
	if target has already exist in the list, just return the old Wrapper.
	this allow to cast a derived-type point to a base-type point. 
	this make multi-inheritance enabled
	else create a new Wrapper
	If a new Wrapper is create and returned, the new Wrapper has reference count 1
	If an exists Wrapper is returned, the reference count is add 1 before return
	*/
	template <typename T>
	CObjectWrapper* WrapObject(void* pRawPointer)
	{
		Lock l;
		if(pRawPointer == NULL)
		{
			// the pointer is a null object
			// this kind of pointer must not be put into the wrapper,
			// use this way to classify embedded pointers and user reachable pointers
			return m_pNullWrapper;
		}

		CObjectWrapper* pW = IsInList(pRawPointer);
		if(NULL == pW)
		{
			// the tempWrapper is not in the set
			// create a new CObjectWrapper to wrapper the raw pointer
			pW = new CSingleObjWrapper(
				pRawPointer, 
				objectDestructor<T>::destroy, 
				sizeof(T));

			Insert((void*)pRawPointer, pW);
		}
		else
		{
			// the wrapper already exists, just add the ref count
			pW->AddRef();
		}

		return pW;
	}

	void Release(void* ptr);

	bool IsEmbeddedPtr(void *ptr);

	// the following methods are used to get the status of the wrapper
	// Set the LinkNode* to 'in use' or not
	void SetPtrInUse(void* ptr, bool bInUse);

	// Get the size of the wrapper object the LinkNode* points
	size_t GetSize(void* ptr);

	// check whether the LinkNode is in-use
	bool IsPtrInUse(void* ptr);

private:
	CWrapperManager();
	~CWrapperManager();

	// to adjust the memory layout
	size_t BlockSize(void *p);
	
	// helper to find whether the ptr is in the wrapper list
	CObjectWrapper* IsInList( void* ptr );
	int IsInObjectScope( void* ptr );

private:
	// TODO: The wrapper here is so great that it deals with deletion of void*
	// the following two struct are the alias of the destroy
	template <typename T>
	struct objectDestructor
	{
		static void destroy(void* obj) 
		{ 
			T* temp = static_cast<T*>(obj); 
#ifdef USING_POOL
			// call the destructor
			temp->~T();
			// use the pool to delete the temp
			CPool::GetInstance()->Free(temp, sizeof(T));
#else
			DELETE_IF_NOT_NULL(temp); 
#endif		
		}
	};
	// TODO: there might be error here
	template <typename T>
	struct arrayDestructor
	{
		static void destroy(void* obj) 
		{
			// TODO: if need to change the delete mode, only here for array object
			T* temp = static_cast<T*>(obj); 
#ifdef USING_POOL
			// use the pool to delete the temp
			CPool::GetInstance()->Free(obj, sizeof(T));
#else
			DELETE_ARRAY_IF_NOT_NULL(temp); 
#endif
		}
	};																				
};